home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Contributed / SpriteWorld / SpriteWorld Files / Utils / SWSounds.c < prev    next >
Encoding:
Text File  |  2000-10-06  |  17.5 KB  |  690 lines  |  [TEXT/CWIE]

  1. ///--------------------------------------------------------------------------------------
  2. // SWSounds.c
  3. //
  4. // Created 11/13/96
  5. ///--------------------------------------------------------------------------------------
  6. #ifndef __TRAPS__
  7. #include <Traps.h>
  8. #endif
  9. #ifndef __RESOURCES__
  10. #include <Resources.h>
  11. #endif
  12.  
  13. #include <SWSounds.h>
  14.  
  15.  
  16. #define kNonZeroValue            1        // used to avoid a bug in the Sound Manager
  17.  
  18.  
  19. typedef struct ChanStruct
  20. {
  21.     SndChannelPtr    channelP;            // Pointer to the sound channel
  22.     short            curSoundID;            // The ID of the sound currently playing in this channel.
  23.     short            volume;                // current volume for this channel
  24.     short            stereoPos;            // stereo position of this channel
  25.     Boolean            isDone;                // Is this channel done playing the last sound?
  26.     Boolean            isAvailable;        // Used by SetChannelAvailability.
  27. } ChanStruct, *ChanStructPtr;
  28.  
  29.  
  30. short        gNumSounds;
  31. short        gNumChannels;                // Number of sound channels
  32. ChanStruct    *gChanArray;                // Channel array that's allocated at run-time
  33. Handle        *gSoundHArray;                // Array of handles to all our sounds
  34. Boolean        gStereoMode = true;            // Whether to use stereo or mono
  35. long        gOldSystemSoundVolume = -1;    // used by SaveSystemVolume and RestoreSystemVolume
  36.  
  37. SndCallBackUPP    gSoundCallBackUPP;        // Needed for PPC
  38.  
  39.  
  40. ///--------------------------------------------------------------------------------------
  41. // IsNewSoundManagerInstalled
  42. ///--------------------------------------------------------------------------------------
  43.  
  44. Boolean IsNewSoundManagerInstalled( void )
  45. {
  46.     NumVersion    theVersion;
  47.     void        *trap1;
  48.     void        *trap2;
  49.     Boolean        installed;
  50.  
  51.     installed = false;
  52.     trap1 = (void *)GetToolTrapAddress(_SoundDispatch);    // SndSoundManagerVersion Trap
  53.     trap2 = (void *)GetToolTrapAddress(_Unimplemented);    // Unimplemented Trap
  54.     
  55.     if (trap1 != trap2)
  56.     {
  57.         theVersion = SndSoundManagerVersion();
  58.         if (theVersion.majorRev >= 3)
  59.         {
  60.             installed = true;
  61.         }
  62.     }
  63.     
  64.     return installed;
  65. }
  66.  
  67.  
  68. ///--------------------------------------------------------------------------------------
  69. // SetSystemVolume
  70. ///--------------------------------------------------------------------------------------
  71.  
  72. void    SetSystemVolume(short volume)        
  73. {
  74.     if (volume < 0)
  75.         volume = 0;
  76.     else if (volume > 7)
  77.         volume = 7;
  78.     
  79.         // Convert from 0-7 to 0-256
  80.     volume = (256 * volume + 6) / 7;    // the + 6 avoids rounding errors
  81.     
  82.         // We set the left and right speaker volume to the same value.
  83.     SetDefaultOutputVolume((long)((long)volume << 16L | (volume & 0xFFFF)));
  84. }
  85.  
  86.  
  87. ///--------------------------------------------------------------------------------------
  88. // GetSystemVolume
  89. ///--------------------------------------------------------------------------------------
  90.  
  91. void    GetSystemVolume(short *volume)        
  92. {
  93.     short    leftVol, rightVol, combinedVol, result;
  94.     long    totalVol;
  95.     
  96.         // We set the left and right speaker volume to the same value.
  97.     GetDefaultOutputVolume(&totalVol);
  98.     
  99.     rightVol = totalVol >> 16L;
  100.     leftVol = totalVol & 0xFFFF;
  101.     
  102.         // Get the combined volume of the left and right speakers
  103.     combinedVol = (rightVol + leftVol) / 2;
  104.     
  105.         // Convert from 0-256 to 0-7
  106.     result = (combinedVol * 7) / 256;
  107.     if (result == 0 && combinedVol > 0)        // Any value above 0 has volume
  108.         result = 1;
  109.     
  110.     *volume = result;
  111. }
  112.  
  113.  
  114. ///--------------------------------------------------------------------------------------
  115. // SaveSystemVolume
  116. ///--------------------------------------------------------------------------------------
  117.  
  118. void    SaveSystemVolume( void )
  119. {
  120.     GetDefaultOutputVolume(&gOldSystemSoundVolume);
  121. }
  122.  
  123.  
  124. ///--------------------------------------------------------------------------------------
  125. // RestoreSystemVolume
  126. ///--------------------------------------------------------------------------------------
  127.  
  128. void    RestoreSystemVolume( void )
  129. {
  130.         // We set the left and right speaker volume to the same value.
  131.     if (gOldSystemSoundVolume != -1)
  132.         SetDefaultOutputVolume(gOldSystemSoundVolume);
  133. }
  134.  
  135.  
  136. ///--------------------------------------------------------------------------------------
  137. // CreateSoundChannels
  138. ///--------------------------------------------------------------------------------------
  139.  
  140. OSErr    CreateSoundChannels(short numChannels)
  141. {
  142.     Size            arraySize;
  143.     short            n;
  144.     OSErr            err = noErr;
  145.     
  146.     gSoundCallBackUPP = NewSndCallBackProc(SoundCallBack);
  147.  
  148.     gNumChannels = numChannels;
  149.     if (numChannels <= 0)
  150.         return -1;
  151.     
  152.         // Allocate memory for gChanArray
  153.     arraySize = (Size)numChannels * sizeof(ChanStruct);
  154.     gChanArray = (ChanStruct *)NewPtrClear(arraySize);
  155.     err = MemError();
  156.     
  157.     
  158.     if (err == noErr)
  159.     {
  160.             // Create the sound channels
  161.         for (n=0; n < numChannels; n++)
  162.         {
  163.             err = SndNewChannel(&gChanArray[n].channelP, sampledSynth, initMono, gSoundCallBackUPP);
  164.             gChanArray[n].channelP->userInfo = SetCurrentA5();
  165.             gChanArray[n].isDone = true;
  166.             gChanArray[n].curSoundID = -1;
  167.             gChanArray[n].volume = kFullVolume;
  168.             gChanArray[n].stereoPos = 0;
  169.             gChanArray[n].isAvailable = true;
  170.             
  171.             if (err)
  172.                 break;
  173.         }
  174.         
  175.         if (err != noErr) // Dispose what we created
  176.         {
  177.             while (n)
  178.             {
  179.                 SndDisposeChannel(gChanArray[--n].channelP, true);
  180.             }
  181.             
  182.             DisposePtr((Ptr)gChanArray);
  183.         }
  184.     }
  185.     
  186.     if (err != noErr)
  187.     {
  188.             // Get rid of our UPPs
  189.         DisposeRoutineDescriptor( gSoundCallBackUPP );
  190.     }
  191.     
  192.     return err;
  193. }
  194.  
  195.  
  196. ///--------------------------------------------------------------------------------------
  197. // DisposeSoundChannels
  198. ///--------------------------------------------------------------------------------------
  199.  
  200. void    DisposeSoundChannels( void )
  201. {
  202.     while (gNumChannels)
  203.     {
  204.         SndDisposeChannel(gChanArray[--gNumChannels].channelP, true);
  205.     }
  206.     
  207.     DisposePtr((Ptr)gChanArray);
  208.     
  209.         // Get rid of our UPPs
  210.     DisposeRoutineDescriptor( gSoundCallBackUPP );
  211. }
  212.  
  213.  
  214. ///--------------------------------------------------------------------------------------
  215. // LoadSounds
  216. ///--------------------------------------------------------------------------------------
  217.  
  218. OSErr    LoadSounds( short startResID, short numSounds )
  219. {
  220.     short        index;
  221.     Size        arraySize;
  222.     OSErr        err = noErr;
  223.     
  224.     gNumSounds = numSounds;
  225.     
  226.         // Allocate memory for gSoundHArray
  227.     arraySize = (Size)numSounds * sizeof(Handle);
  228.     gSoundHArray = (Handle *)NewPtrClear(arraySize);
  229.     err = MemError();
  230.     
  231.     if (err == noErr)
  232.     {
  233.             // Load the sounds
  234.         for (index = 0; index < numSounds && err != memFullErr; index++)
  235.         {
  236.             gSoundHArray[index] = GetResource('snd ', startResID + index);
  237.             
  238.             if (gSoundHArray[index] != NULL)
  239.             {
  240.                 HLock(gSoundHArray[index]);
  241.             }
  242.             else
  243.             {
  244.                     // If old err was a memory error, don't replace it with resNotFound
  245.                 if (err == noErr || err == resNotFound)
  246.                     err = ResError() ? ResError() : resNotFound;
  247.             }
  248.         }
  249.     }
  250.     
  251.     return err;
  252. }
  253.  
  254.  
  255. ///--------------------------------------------------------------------------------------
  256. // DisposeSounds
  257. ///--------------------------------------------------------------------------------------
  258.  
  259. void    DisposeSounds( void )
  260. {
  261.     short    index;
  262.     
  263.     for (index = 0; index < gNumSounds; index++)
  264.     {
  265.         ReleaseResource(gSoundHArray[index]);
  266.     }
  267.     
  268.     DisposePtr((Ptr)gSoundHArray);
  269.  
  270. }
  271.  
  272.  
  273. ///--------------------------------------------------------------------------------------
  274. // PlaySound - starts playing a sound in the appropriate channel
  275. ///--------------------------------------------------------------------------------------
  276.  
  277. void    PlaySound(
  278.     short soundID, 
  279.     short channelNum, 
  280.     PlayType playType)
  281. {
  282.     PlaySound2(soundID, channelNum, playType, 256, 0, k22khz, false);
  283. }
  284.  
  285.  
  286. ///--------------------------------------------------------------------------------------
  287. // PlaySound2 - starts playing a sound in the appropriate channel
  288. ///--------------------------------------------------------------------------------------
  289.  
  290. void    PlaySound2(
  291.     short soundID, 
  292.     short channelNum, 
  293.     PlayType playType,
  294.     short volume,
  295.     short stereoPosition,
  296.     double rate,
  297.     Boolean doLoopingSound)
  298. {
  299.     #pragma        unused(doLoopingSound)
  300.     
  301.     if (soundID < 0 || soundID >= gNumSounds || gSoundHArray[soundID] == NULL)
  302.         return;
  303.     
  304.     channelNum = FindChannel(soundID, channelNum, playType);
  305.     
  306.     SetChannelVolume(channelNum, volume);
  307.     SetStereoPosition(channelNum, stereoPosition);
  308.     PlayMySound(soundID, channelNum);
  309.     
  310.     if (rate != 1.0)
  311.         SetChannelRate(channelNum, rate);
  312. }
  313.  
  314.  
  315. ///--------------------------------------------------------------------------------------
  316. // FindChannel - used by PlaySound2
  317. ///--------------------------------------------------------------------------------------
  318.  
  319. short    FindChannel(short soundID, short channelNum, PlayType playType)
  320. {
  321.     Boolean        allChannelsAreBusy;
  322.     short        emptyChannelNum;
  323.     short        n;
  324.     
  325.     if (channelNum < 0 || channelNum >= gNumChannels)
  326.         channelNum = 0;
  327.     
  328.  
  329.     if (playType == kPlaySoundInChannel)
  330.     {
  331.         return channelNum;
  332.     }
  333.     else if (playType == kReplaceSameSound)
  334.     {
  335.         for (n=0; n < gNumChannels; n++)    // Look for our sound in all channels
  336.         {
  337.             if (gChanArray[n].curSoundID == soundID)
  338.             {
  339.                 return n;
  340.             }
  341.         }
  342.     }
  343.     
  344.         // The following code is executed if playType is kFindEmptyChannel or if
  345.         // playType was kReplaceSameSound and the sound was not found in any channel.
  346.     
  347.     
  348.         // Determine if all channels are busy
  349.     allChannelsAreBusy = true;
  350.     for (n=0; n < gNumChannels; n++)
  351.     {
  352.         if (gChanArray[n].isDone && gChanArray[n].isAvailable)
  353.         {
  354.             allChannelsAreBusy = false;
  355.             emptyChannelNum = n;
  356.             break;
  357.         }
  358.     }
  359.     
  360.     
  361.     if (gChanArray[channelNum].isDone)
  362.     {
  363.         return channelNum;        // The requested channel is free
  364.     }
  365.     else if (allChannelsAreBusy)
  366.     {
  367.             // First search all other channels for our sound
  368.         for (n=0; n < gNumChannels; n++)
  369.         {
  370.             if (gChanArray[n].curSoundID == soundID)
  371.             {
  372.                 return n;
  373.             }
  374.         }
  375.         
  376.             // If none are found, return the requested channel
  377.         return channelNum;    
  378.     }
  379.     else
  380.     {
  381.         return emptyChannelNum;        // Return an empty channel
  382.     }
  383. }
  384.  
  385.  
  386. ///--------------------------------------------------------------------------------------
  387. // PlayMySound - called by PlaySound
  388. ///--------------------------------------------------------------------------------------
  389.  
  390. void    PlayMySound(short soundID, short channelNum)
  391. {
  392.     SndCommand    theCommand;
  393.     
  394.         // Stop any sound already playing in this channel
  395.     StopChannel(channelNum);
  396.  
  397.         // Play the sound
  398.     gChanArray[channelNum].isDone = false;
  399.     gChanArray[channelNum].curSoundID = soundID;
  400.     SndPlay(gChanArray[channelNum].channelP, (SndListHandle)gSoundHArray[soundID], true);
  401.  
  402.         // Install the callBack
  403.     theCommand.cmd = callBackCmd;
  404.     theCommand.param1 = kNonZeroValue;
  405.     theCommand.param2 = channelNum;
  406.     SndDoCommand(gChanArray[channelNum].channelP, &theCommand, false);
  407. }
  408.  
  409. #pragma mark -
  410. ///--------------------------------------------------------------------------------------
  411. // StopMySound
  412. ///--------------------------------------------------------------------------------------
  413.  
  414. void    StopMySound(short soundID)
  415. {
  416.     short n;
  417.     
  418.     if (soundID < 0 || soundID >= gNumSounds)
  419.         return;
  420.     
  421.     for (n=0; n < gNumChannels; n++)
  422.     {
  423.         if (gChanArray[n].curSoundID == soundID)
  424.         {
  425.             StopChannel(n);
  426.         }
  427.     }
  428. }
  429.  
  430.  
  431. ///--------------------------------------------------------------------------------------
  432. // StopChannel
  433. ///--------------------------------------------------------------------------------------
  434.  
  435. void    StopChannel(short channelNum)
  436. {
  437.     SndCommand    theCommand;
  438.     
  439.     if (channelNum < 0 || channelNum >= gNumChannels)
  440.         return;
  441.     
  442.         // Stop the sound already playing in this channel
  443.     if (!gChanArray[channelNum].isDone)
  444.     {
  445.         theCommand.cmd = quietCmd;
  446.         SndDoImmediate(gChanArray[channelNum].channelP, &theCommand);
  447.         theCommand.cmd = flushCmd;
  448.         SndDoImmediate(gChanArray[channelNum].channelP, &theCommand);
  449.         gChanArray[channelNum].isDone = true;
  450.         gChanArray[channelNum].curSoundID = -1;
  451.     }
  452. }
  453.  
  454.  
  455. ///--------------------------------------------------------------------------------------
  456. // SetChannelVolume
  457. ///--------------------------------------------------------------------------------------
  458.  
  459. void    SetChannelVolume(short channelNum, short newVolume)
  460. {
  461.     if (channelNum < 0 || channelNum >= gNumChannels)
  462.         return;
  463.     
  464.     if (gChanArray[channelNum].volume != newVolume)
  465.     {
  466.         SetVolumeAndStereoPosition(channelNum, newVolume, gChanArray[channelNum].stereoPos);
  467.     }
  468. }
  469.  
  470. ///--------------------------------------------------------------------------------------
  471. // SetChannelRate - pass 1.0 as newPitch for normal playback, 2.0 for double speed,
  472. // and 0.5 for half speed. Call this *after* starting a sound in the channel.
  473. ///--------------------------------------------------------------------------------------
  474.  
  475. void    SetChannelRate(short channelNum, double newPitch)
  476. {
  477.     SndCommand        theCommand;
  478.     
  479.     theCommand.cmd = rateCmd;
  480.     theCommand.param1 = 0;
  481.     theCommand.param2 = (double)newPitch * 65536;
  482.     
  483.     SndDoImmediate(gChanArray[channelNum].channelP, &theCommand);
  484. }
  485.  
  486.  
  487. ///--------------------------------------------------------------------------------------
  488. // SetStereoPosition
  489. ///--------------------------------------------------------------------------------------
  490.  
  491. void    SetStereoPosition(short channelNum, short stereoPosition)
  492. {
  493.     if (channelNum < 0 || channelNum >= gNumChannels)
  494.         return;
  495.     
  496.     if (gChanArray[channelNum].stereoPos != stereoPosition)
  497.     {
  498.         SetVolumeAndStereoPosition(channelNum, gChanArray[channelNum].volume, stereoPosition);
  499.     }
  500. }
  501.  
  502.  
  503. ///--------------------------------------------------------------------------------------
  504. // SetVolumeAndStereoPosition - used internally.
  505. ///--------------------------------------------------------------------------------------
  506.  
  507. void    SetVolumeAndStereoPosition(short channelNum, short newVolume, short stereoPosition)
  508. {
  509.     short            leftVolume, rightVolume, stereoOffset;
  510.     SndCommand        theCommand;
  511.     
  512.     if (channelNum < 0 || channelNum >= gNumChannels)
  513.         return;
  514.     
  515.     if (newVolume < 0)
  516.         newVolume = 0;
  517.         
  518.     if (stereoPosition < -256)
  519.         stereoPosition = -256;
  520.     else if (stereoPosition > 256)
  521.         stereoPosition = 256;
  522.     
  523.     gChanArray[channelNum].volume = newVolume;
  524.     gChanArray[channelNum].stereoPos = stereoPosition;
  525.     
  526.     if (gStereoMode == true)
  527.     {
  528.             // Calculate stereoOffset based on stereoPosition and the current volume
  529.         stereoOffset = (newVolume * stereoPosition) / 256;
  530.         
  531.         leftVolume = newVolume - stereoOffset;
  532.         rightVolume = newVolume + stereoOffset;
  533.     }
  534.     else
  535.     {
  536.             // Mono sound
  537.         leftVolume = newVolume;
  538.         rightVolume = newVolume;
  539.     }
  540.  
  541.         // Issue the command
  542.     theCommand.param1 = 0;
  543.     theCommand.param2 = (long)((long)rightVolume << 16L | (leftVolume & 0xFFFF));
  544.     theCommand.cmd = volumeCmd;
  545.     SndDoImmediate(gChanArray[channelNum].channelP, &theCommand);
  546. }
  547.  
  548.  
  549. ///--------------------------------------------------------------------------------------
  550. // SetChannelAvailability
  551. ///--------------------------------------------------------------------------------------
  552.  
  553. void    SetChannelAvailability(short channelNum, Boolean isAvailable)
  554. {
  555.     if (channelNum < 0 || channelNum >= gNumChannels)
  556.         return;
  557.     
  558.     gChanArray[channelNum].isAvailable = isAvailable;
  559. }
  560.  
  561.  
  562. ///--------------------------------------------------------------------------------------
  563. // SetStereoMode
  564. ///--------------------------------------------------------------------------------------
  565.  
  566. void    SetStereoMode(Boolean mode)
  567. {
  568.     short    n;
  569.     
  570.     gStereoMode = mode;
  571.     
  572.         // Update all channels for the current mode
  573.     for (n = 0; n < gNumChannels; n++)
  574.     {
  575.         SetVolumeAndStereoPosition(n, gChanArray[n].volume, gChanArray[n].stereoPos);
  576.     }
  577. }
  578.  
  579.  
  580. ///--------------------------------------------------------------------------------------
  581. //   GetStereoPositionOfSprite
  582. ///--------------------------------------------------------------------------------------
  583.  
  584. short    GetStereoPositionOfSprite(SpritePtr srcSpriteP, Rect *destRectP)
  585. {
  586.     short    col, stereoPos;
  587.     
  588.     col = srcSpriteP->destFrameRect.left + SW_RECT_WIDTH(srcSpriteP->destFrameRect) / 2;
  589.     
  590.     stereoPos = GetStereoPositionOfColumn(col, destRectP);
  591.     
  592.     return stereoPos;
  593. }
  594.  
  595.  
  596. ///--------------------------------------------------------------------------------------
  597. //   GetStereoPositionOfColumn
  598. ///--------------------------------------------------------------------------------------
  599.  
  600. short    GetStereoPositionOfColumn(long col, Rect *backRectP)
  601. {
  602.     Rect    backRect = *backRectP;
  603.     long    screenMidCol;
  604.     short    offset, stereoPos;
  605.     
  606.         // Make sure the left side starts at 0
  607.     if (backRect.left != 0)
  608.     {
  609.         offset = backRect.left;
  610.         backRect.left -= offset;
  611.         backRect.right -= offset;
  612.         col -= offset;
  613.     }
  614.     
  615.     screenMidCol = backRect.right / 2;
  616.     
  617.         // Fix col if out of bounds
  618.     if (col < 0)
  619.         col = 0;
  620.     else if (col > backRect.right)
  621.         col = backRect.right;
  622.     
  623.         // Calculate it
  624.     col -= screenMidCol;
  625.     stereoPos = (col * 256) / screenMidCol;
  626.     
  627.     return stereoPos;
  628. }
  629.  
  630.  
  631. ///--------------------------------------------------------------------------------------
  632. // FindSound - searches all channels for soundID. Returns channel number, or -1.
  633. ///--------------------------------------------------------------------------------------
  634.  
  635. short    FindSound(short soundID)
  636. {
  637.     short        channel;
  638.     Boolean        foundSound;
  639.  
  640.     foundSound = false;
  641.     for (channel = 0; channel < gNumChannels; channel++)
  642.     {
  643.         if (gChanArray[channel].curSoundID == soundID)
  644.         {
  645.             foundSound = true;
  646.             break;
  647.         }
  648.     }
  649.     
  650.     if (foundSound)
  651.         return channel;
  652.     else
  653.         return -1;
  654. }
  655.  
  656.  
  657. ///--------------------------------------------------------------------------------------
  658. // SoundCallBack - called when a channel is done playing a sound
  659. ///--------------------------------------------------------------------------------------
  660.  
  661. pascal void SoundCallBack(SndChannelPtr theChannel, SndCommand *theCommand)
  662. {
  663.     short        channelNum;
  664.     long        saveA5;
  665.  
  666. #if SW_PPC
  667.     #pragma unused(saveA5, theChannel)
  668. #else
  669.     saveA5 = SetA5(theChannel->userInfo);
  670. #endif
  671.  
  672.         // We check for kNonZeroValue to avoid a bug in some versions of the Sound Manager.
  673.     if (theCommand->param1 == kNonZeroValue)
  674.     {
  675.         channelNum = theCommand->param2;
  676.         
  677.         if (channelNum >= 0 && channelNum < gNumChannels)
  678.         {
  679.             gChanArray[channelNum].isDone = true;
  680.             gChanArray[channelNum].curSoundID = -1;
  681.         }
  682.     }
  683.     
  684.             
  685. #if !SW_PPC
  686.     SetA5(saveA5);
  687. #endif
  688. }
  689.  
  690.